﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Reflection;
using System.Web;
using Microsoft.VisualBasic;

namespace TheCup
{
    // ? [JsonObject(IsReference = true)]
    [Table("score")]
    public class Scorecard
    {
        public int Id { get; set; }
        
        [Column("roundno")]
        public int RoundId { get; set; }

        [ForeignKey("RoundId")] // auto by name?
        public virtual Round Round { get; set; }
        
        public int TeeId { get; set; }
        
        [ForeignKey("TeeId")]
        public virtual Tee Tee { get; set; }
        
        [Column("playerno")]
        public int PlayerId { get; set; }

        [ForeignKey("PlayerId")]
        public virtual Player Player { get; set; }

        [Column("hcp")]
        public double Handicap { get; set; }
        
        [Column("putts"), DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed)]
        public decimal? DbPutts { get; set; }

        [Column("strokes"), DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed)]
        public decimal? DbStrokes { get; set; }

        public Nullable<decimal> S1 { get; set; }
        public Nullable<decimal> S2 { get; set; }
        public Nullable<decimal> S3 { get; set; }
        public Nullable<decimal> S4 { get; set; }
        public Nullable<decimal> S5 { get; set; }
        public Nullable<decimal> S6 { get; set; }
        public Nullable<decimal> S7 { get; set; }
        public Nullable<decimal> S8 { get; set; }
        public Nullable<decimal> S9 { get; set; }
        public Nullable<decimal> S10 { get; set; }
        public Nullable<decimal> S11 { get; set; }
        public Nullable<decimal> S12 { get; set; }
        public Nullable<decimal> S13 { get; set; }
        public Nullable<decimal> S14 { get; set; }
        public Nullable<decimal> S15 { get; set; }
        public Nullable<decimal> S16 { get; set; }
        public Nullable<decimal> S17 { get; set; }
        public Nullable<decimal> S18 { get; set; }

        public Nullable<decimal> P1 { get; set; }
        public Nullable<decimal> P2 { get; set; }
        public Nullable<decimal> P3 { get; set; }
        public Nullable<decimal> P4 { get; set; }
        public Nullable<decimal> P5 { get; set; }
        public Nullable<decimal> P6 { get; set; }
        public Nullable<decimal> P7 { get; set; }
        public Nullable<decimal> P8 { get; set; }
        public Nullable<decimal> P9 { get; set; }
        public Nullable<decimal> P10 { get; set; }
        public Nullable<decimal> P11 { get; set; }
        public Nullable<decimal> P12 { get; set; }
        public Nullable<decimal> P13 { get; set; }
        public Nullable<decimal> P14 { get; set; }
        public Nullable<decimal> P15 { get; set; }
        public Nullable<decimal> P16 { get; set; }
        public Nullable<decimal> P17 { get; set; }
        public Nullable<decimal> P18 { get; set; }



        #region HelperMethods
        
        public int GetPlayingHcp()
        {
            return (int)Math.Round((this.Handicap * this.Tee.Slope / 113) + this.Tee.CR - this.Tee.Par, 0);
        }

        //public int PlayingHandicap
        //{
        //    get
        //    {
        //        if (Tee == null)
        //            throw new Exception("Tee was null");

        //        return (int)Math.Round((this.Handicap * this.Tee.Slope / 113) + this.Tee.CR - this.Tee.Par, 0);
        //    }
        //}

        public Nullable<decimal> Score
        {
            get
            {
                return ScoreOut + ScoreIn;
            }
        }

        public Nullable<decimal> ScoreOut
        {
            get
            {
                return S1.GetValueOrDefault() + S2.GetValueOrDefault() + S3.GetValueOrDefault() + S4.GetValueOrDefault() + S5.GetValueOrDefault() + S6.GetValueOrDefault() + S7.GetValueOrDefault() + S8.GetValueOrDefault() + S9.GetValueOrDefault();
            }
        }

        public Nullable<decimal> ScoreIn
        {
            get
            {
                return S10.GetValueOrDefault() + S11.GetValueOrDefault() + S12.GetValueOrDefault() + S13.GetValueOrDefault() + S14.GetValueOrDefault() + S15.GetValueOrDefault() + S16.GetValueOrDefault() + S17.GetValueOrDefault() + S18.GetValueOrDefault();
            }
        }

        public Nullable<decimal> NettoScore
        {
            get
            {
                return ScoreOut + ScoreIn - GetPlayingHcp();
            }
        }

        public Nullable<decimal> NettoScoreVersusPar
        {
            get
            {
                return NettoScore - this.Tee.Par;
            }
        }

        public string NettoScoreVersusParPrintable
        {
            get
            {
                if (NettoScoreVersusPar == 0) return "E";
                return NettoScoreVersusPar < 0 ? NettoScoreVersusPar.ToString() : string.Format("+{0}", NettoScoreVersusPar);
            }
        }

        public Nullable<decimal> ScoreVersusPar
        {
            get
            {
                return Score - this.Tee.Par;
            }
        }

        /// <summary>
        /// Amount of strokes for a specific hole
        /// </summary>
        /// <param name="hole"></param>
        /// <returns></returns>
        public int GetStrokesForHole(int hole)
        {
            return PropertyForHole("S", hole);
        }

        /// <summary>
        /// Amount of putts for a specific hole
        /// </summary>
        /// <param name="hole"></param>
        /// <returns></returns>
        public int GetPuttsForHole(int hole)
        {
            return PropertyForHole("P", hole);
        }


        public void SetStrokesForHole(int hole, int strokes)
        {
            GetPropertyInfo("S", hole).SetValue(this, decimal.Parse(strokes.ToString()));
        }

        public void SetPuttsForHole(int hole, int putts)
        {
            GetPropertyInfo("P", hole).SetValue(this, decimal.Parse(putts.ToString()));
        }

        /// <summary>
        /// S = Score, STBL = Stableford
        /// P = Putts, 
        /// </summary>
        /// <param name="property"></param>
        /// <param name="hole"></param>
        /// <returns></returns>
        public int PropertyForHole(string property, int hole)
        {
            object result = GetPropertyInfo(property, hole).GetValue(this);
            return Int32.Parse(result.ToString());
        }

        public PropertyInfo GetPropertyInfo(string property, int hole)
        {
            return this.GetType().GetProperty(property + hole);
        }

        public string ScoreCssClass(int hole)
        {
            int score = GetBruttoScoreVsPar(hole);
            string result = string.Empty;
            switch (score)
            {
                case 2:
                    result = "double";
                    break;
                case 1:
                    result = "bogey";
                    break;
                case 0:
                    result = "par";
                    break;
                case -1:
                    result = "birdie";
                    break;
                case -2:
                    result = "eagle";
                    break;
                case -3:
                    result = "albatros";
                    break;
                default:
                    result = "worse";
                    break;
            }
            return result;
        }

        internal object GetPropertyValue(string property, int hole)
        {
            PropertyInfo pi = this.GetType().GetProperty(property + hole);
            return pi.GetValue(this);
        }

        public int GetStablefordPoints(int hole)
        {
            // hate this shit
            if (Tee == null)
                return 0;
            int par = Tee.GetPar(hole);
            int strokes = GetStrokesForHole(hole);
            int assigned = GetAssignedStrokes(hole);
            int result = par - strokes + assigned + 2;
            return (result < 0) ? 0 : result;
        }

        public int GetBruttoScoreVsPar(int hole)
        {
            return GetStrokesForHole(hole) - Tee.GetPar(hole);
        }

        public int GetNettoScoreVsPar(int hole)
        {
            return GetStrokesForHole(hole) - Tee.GetPar(hole) - GetAssignedStrokes(hole);
        }

        public int GetAssignedStrokes(int hole)
        {
            return (int)Math.Ceiling((double)(GetPlayingHcp() - Tee.GetIdx(hole) + 1) / 18);
        }

        public bool GIRForHole(int hole)
        {
            if (Tee == null)
                return false;
            return (GetPuttsForHole(hole) - GetStrokesForHole(hole) + Tee.GetPar(hole)) > 1;
        }

        public string GetSPHString(int hole)
        {
            int sph = GetAssignedStrokes(hole);

            // gawd, probably an easier way :)
            string output = string.Empty;
            for (int i = 0; i < sph; i++)
            {
                output += "I";
            }
            return output;
        }

        // can be updated by round class to set position by nettoscore
        [NotMapped]
        public int PositionByNettoscore { get; set; }

        [NotMapped]
        public string PositionByNettoscorePrintable { get; set; }

        [NotMapped]
        public decimal TrophyListPoints { get; set; }

        [NotMapped]
        public decimal OrderOfMeritPoints { get; set; }

        public decimal WorldRankingPointsByDate(DateTime baseDate)
        {
            //DateAndTime.DateDiff(DateInterval.WeekOfYear,)
            // (CONVERT([decimal](18,3),(3)-abs(datediff(week,[DateEntry],getdate()))*(0.028),(0))*[Points])
            //double weeks = Math.Abs(baseDate.Date.Subtract(this.Round.DateEntry.Date).TotalDays / 7);
            int weeks = Convert.ToInt32(Math.Round(Math.Abs(this.Round.DateEntry.Date.Subtract(baseDate.Date).TotalDays / 7)));
            double basePoints = 3;
            return Convert.ToDecimal((basePoints - (weeks * 0.028)) * (double)this.TrophyListPoints);
        }

        public int Putts
        {
            get { return PuttsOut + PuttsIn; }
        }

        public int PuttsOut
        {
            get { return (int)(P1.GetValueOrDefault() + P2.GetValueOrDefault() + P3.GetValueOrDefault() + P4.GetValueOrDefault() + P5.GetValueOrDefault() + P6.GetValueOrDefault() + P7.GetValueOrDefault() + P8.GetValueOrDefault() + P9.GetValueOrDefault()); }
        }

        public int PuttsIn
        {
            get { return (int)(P10.GetValueOrDefault() + P11.GetValueOrDefault() + P12.GetValueOrDefault() + P13.GetValueOrDefault() + P14.GetValueOrDefault() + P15.GetValueOrDefault() + P16.GetValueOrDefault() + P17.GetValueOrDefault() + P18.GetValueOrDefault()); }
        }


        private int _girOut;

        public int GIROut
        {
            get
            {
                if (_girOut == 0)
                {
                    for (int i = 1; i < 10; i++)
                        _girOut += GIRForHole(i) ? 1 : 0;
                }
                return _girOut;
            }
        }

        private int _girIn;

        public int GIRIn
        {
            get
            {
                if (_girIn == 0)
                {
                    for (int i = 10; i < 19; i++)
                        _girIn += GIRForHole(i) ? 1 : 0;
                }
                return _girIn;
            }
        }

        public int GIRTotal { get { return GIROut + GIRIn; } }

        public double GIRPercentage { get { return GIRTotal * 100 / 18; } }

        
        public int StablefordPoints
        {
            get
            {
                if (Tee == null)
                    return 0;
                return StablefordPointsOut + StablefordPointsIn;
            }
        }

        public int StablefordPointsOut
        {
            get
            {
                int res = 0;
                for (int i = 1; i < 10; i++)
                    res += GetStablefordPoints(i);
                return res;
            }
        }

        public int StablefordPointsIn
        {
            get
            {
                int res = 0;
                for (int i = 10; i < 19; i++)
                    res += GetStablefordPoints(i);
                return res;
            }
        }

        #endregion
    }

}